package com.mlethe.library.calendarview;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.text.TextUtils;
import android.util.AttributeSet;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;

/**
 * Google规范化的属性委托
 *
 * @Author Mlethe
 * @Date 2022/12/29
 */
final class CalendarViewDelegate {

    /**
     * 自定义当前时间
     */
    private final CustomTime mCustomTime = new CustomTime();

    /**
     * 周起始：周日
     */
    static final int WEEK_START_WITH_SUN = 1;

    /**
     * 周起始：周一
     */
    static final int WEEK_START_WITH_MON = 2;

    /**
     * 周起始：周六
     */
    static final int WEEK_START_WITH_SAT = 7;

    /**
     * 周起始
     */
    private int mWeekStart;

    /**
     * 切换月份不选中，滚动到指定日期才会选中
     */
    static final int CHANGE_MONTH_SELECT_NONE = -1;

    /**
     * 默认选择当月第一天 first_day_of_month
     */
    static final int FIRST_DAY_OF_MONTH = 0;

    /**
     * 跟随上个月 last_select_day
     */
    static final int LAST_MONTH_VIEW_SELECT_DAY = 1;

    /**
     * 跟随上个月 last_select_day_ignore_current 忽视今天
     */
    static final int LAST_MONTH_VIEW_SELECT_DAY_IGNORE_CURRENT = 2;

    /**
     * 默认天的选择的模式
     */
    private int mDefaultCalendarSelectMode;

    /**
     * 全部显示
     */
    static final int MODE_ALL_MONTH = 0;

    /**
     * 仅显示当前月份
     */
    static final int MODE_ONLY_CURRENT_MONTH = 1;

    /**
     * 自适应显示，不会多出一行，但是会自动填充
     */
    static final int MODE_FIT_MONTH = 2;

    /**
     * 月份显示模式
     */
    private int mMonthViewShowMode;

    /**
     * 支持转换的最小年份
     */
    static final int MIN_YEAR = 1900;
    /**
     * 支持转换的最大年份
     */
    private static final int MAX_YEAR = 2099;

    /**
     * 最小年份和最大年份
     */
    private int mMinYear, mMaxYear;

    /**
     * 最小年份和最大年份对应最小月份和最大月份
     * when you want set 2015-07 to 2017-08
     */
    private int mMinYearMonth, mMaxYearMonth;

    /**
     * 月起始日
     */
    private int mMonthStartDay;

    /**
     * 当前年月（根据月起始日计算后的）
     */
    private int mCurrentYear, mCurrentMonth;

    /**
     * 今天的日子
     */
    private Calendar mCurrentDate;

    /**
     * 标记的日期,数量巨大，请使用这个
     */
    Map<String, Calendar> mSchemeDatesMap;

    /**
     * 日历卡的项高度
     */
    private int mCalendarItemHeight;

    /**
     * 日历内部左padding
     */
    private int mCalendarPaddingLeft;

    /**
     * 日历内部右padding
     */
    private int mCalendarPaddingRight;

    /**
     * 日期文本大小
     */
    private final int mDayTextSize;

    /**
     * 各种字体颜色，看名字知道对应的地方
     */
    private int mCurDayTextColor,
            mWeekTextColor,
            mSchemeTextColor,
            mOtherMonthTextColor,
            mCurrentMonthTextColor,
            mSelectedTextColor;

    /**
     * 标记的主题色和选中的主题色
     */
    private int mSchemeThemeColor, mSelectedThemeColor;

    /**
     * 星期栏的背景的背景
     */
    private int mWeekBackground;

    /**
     * 星期栏字体大小
     */
    private int mWeekTextSize;

    /**
     * 星期栏的高度
     */
    private int mWeekBarHeight;

    /**
     * 是否是周选择
     */
    private boolean mIsWeekSelector;

    /**
     * 月视图类
     */
    private Class<?> mMonthViewClass;

    /**
     * 自定义周栏
     */
    private Class<?> mWeekBarClass;

    /**
     * 保存选中的日期
     */
    private Calendar mSelectedCalendar;

    /**
     * 当前周开始时间戳，结束时间戳
     */
    private long weekStartTime, weekEndTime;

    /**
     * 月份切换事件
     */
    CalendarView.OnMonthChangeListener mMonthChangeListener;

    /**
     * 日期选中监听
     */
    CalendarView.OnCalendarSelectListener mCalendarSelectListener;

    /**
     * 外部日期长按事件
     */
    CalendarView.OnCalendarLongClickListener mCalendarLongClickListener;

    /**
     * 周选择事件
     */
    CalendarView.OnWeekSelectListener mWeekSelectListener;

    /**
     * 周长按事件
     */
    CalendarView.OnWeekLongClickListener mWeekLongClickListener;

    /**
     * 滑动超限事件
     */
    CalendarView.OnScrollOutRangeListener mScrollOutRangeListener;

    CalendarViewDelegate(@NonNull Context context, @Nullable AttributeSet attrs) {
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CalendarView);
        int calendarPadding = (int) array.getDimension(R.styleable.CalendarView_calendar_padding, 0);
        mCalendarPaddingLeft = (int) array.getDimension(R.styleable.CalendarView_calendar_padding_left, 0);
        mCalendarPaddingRight = (int) array.getDimension(R.styleable.CalendarView_calendar_padding_right, 0);

        if (calendarPadding != 0) {
            mCalendarPaddingLeft = calendarPadding;
            mCalendarPaddingRight = calendarPadding;
        }
        mSchemeTextColor = array.getColor(R.styleable.CalendarView_scheme_text_color, 0xFFFFFFFF);
        mSchemeThemeColor = array.getColor(R.styleable.CalendarView_scheme_theme_color, 0x50CFCFCF);
        String weekBarClassPath = array.getString(R.styleable.CalendarView_week_bar_view);
        String monthViewClassPath = array.getString(R.styleable.CalendarView_month_view);
        mWeekTextSize = array.getDimensionPixelSize(R.styleable.CalendarView_week_text_size,
                CalendarUtil.dipToPx(context, 12));
        mWeekBarHeight = (int) array.getDimension(R.styleable.CalendarView_week_bar_height,
                CalendarUtil.dipToPx(context, 40));

        mDefaultCalendarSelectMode = array.getInt(R.styleable.CalendarView_month_view_auto_select_mode,
                FIRST_DAY_OF_MONTH);
        mMonthViewShowMode = array.getInt(R.styleable.CalendarView_month_view_show_mode, MODE_FIT_MONTH);
        mWeekStart = array.getInt(R.styleable.CalendarView_week_start_with, WEEK_START_WITH_SUN);

        mWeekBackground = array.getColor(R.styleable.CalendarView_week_background, Color.TRANSPARENT);
        mWeekTextColor = array.getColor(R.styleable.CalendarView_week_text_color, 0xFF333333);

        mCurDayTextColor = array.getColor(R.styleable.CalendarView_current_day_text_color, Color.RED);

        mSelectedThemeColor = array.getColor(R.styleable.CalendarView_selected_theme_color, 0x50CFCFCF);
        mSelectedTextColor = array.getColor(R.styleable.CalendarView_selected_text_color, 0xFF111111);

        mCurrentMonthTextColor = array.getColor(R.styleable.CalendarView_current_month_text_color, 0xFF111111);
        mOtherMonthTextColor = array.getColor(R.styleable.CalendarView_other_month_text_color, 0xFFe1e1e1);

        mMinYear = array.getInt(R.styleable.CalendarView_min_year, MIN_YEAR);
        mMaxYear = array.getInt(R.styleable.CalendarView_max_year, MAX_YEAR);
        mMinYearMonth = array.getInt(R.styleable.CalendarView_min_year_month, 1);
        mMaxYearMonth = array.getInt(R.styleable.CalendarView_max_year_month, 12);
        mMonthStartDay = array.getInt(R.styleable.CalendarView_month_start_day, 1);

        mDayTextSize = array.getDimensionPixelSize(R.styleable.CalendarView_day_text_size,
                CalendarUtil.dipToPx(context, 16));
        mCalendarItemHeight = (int) array.getDimension(R.styleable.CalendarView_calendar_item_height,
                CalendarUtil.dipToPx(context, 56));

        try {
            mWeekBarClass = TextUtils.isEmpty(weekBarClassPath) ? WeekBar.class : Class.forName(weekBarClassPath);
        } catch (Exception e) {
            e.printStackTrace();
            mWeekBarClass = WeekBar.class;
        }

        try {
            mMonthViewClass = TextUtils.isEmpty(monthViewClassPath) ? DefaultMonthView.class : Class.forName(monthViewClassPath);
        } catch (Exception e) {
            e.printStackTrace();
            mMonthViewClass = DefaultMonthView.class;
        }
        array.recycle();

        if (WeekMonthView.class.isAssignableFrom(mMonthViewClass)) {
            mIsWeekSelector = true;
        }

        if (mMonthStartDay < 1) {
            mMonthStartDay = 1;
        } else if (mMonthStartDay > 31) {
            mMonthStartDay = 31;
        }
        if (mMinYear <= MIN_YEAR) {
            mMinYear = MIN_YEAR;
        }
        updateCurrentDay();
        mSelectedCalendar = mCurrentDate;
        init();
        int maxYear = mCurrentYear + 100;
        if (maxYear <= MAX_YEAR) {
            mMaxYear = MAX_YEAR;
        } else if (mMaxYear >= maxYear) {
            mMaxYear = maxYear;
        } else if (mMaxYear < mCurrentYear) {
            mMaxYear = mCurrentDate.getYear();
        }
    }

    /**
     * 初始化
     */
    private void init() {
        java.util.Calendar calendar = java.util.Calendar.getInstance();
        calendar.clear();
        calendar.setTimeInMillis(System.currentTimeMillis());
        int actualMaximum = calendar.getActualMaximum(java.util.Calendar.DATE);
        int monthStartDay = mMonthStartDay;
        if (actualMaximum < monthStartDay) {
            monthStartDay = actualMaximum;
        }
        int day = calendar.get(java.util.Calendar.DAY_OF_MONTH);
        if (day < monthStartDay) {
            int moth = calendar.get(java.util.Calendar.MONTH);
            calendar.set(java.util.Calendar.DAY_OF_MONTH, 1);
            calendar.set(java.util.Calendar.MONTH, moth - 1);
            actualMaximum = calendar.getActualMaximum(java.util.Calendar.DATE);
            calendar.set(java.util.Calendar.DAY_OF_MONTH, Math.min(day, actualMaximum));
        }
        mCurrentYear = calendar.get(java.util.Calendar.YEAR);
        mCurrentMonth = calendar.get(java.util.Calendar.MONTH) + 1;
    }

    void setRange(int minYear, int minYearMonth,
                  int maxYear, int maxYearMonth) {
        this.mMinYear = minYear;
        this.mMinYearMonth = minYearMonth;
        this.mMaxYear = maxYear;
        this.mMaxYearMonth = maxYearMonth;
    }

    void setTextColor(int curDayTextColor, int curMonthTextColor, int otherMonthTextColor) {
        mCurDayTextColor = curDayTextColor;
        mOtherMonthTextColor = otherMonthTextColor;
        mCurrentMonthTextColor = curMonthTextColor;
    }

    void setSchemeColor(int schemeColor, int schemeTextColor) {
        this.mSchemeThemeColor = schemeColor;
        this.mSchemeTextColor = schemeTextColor;
    }

    void setSelectColor(int selectedColor, int selectedTextColor) {
        this.mSelectedThemeColor = selectedColor;
        this.mSelectedTextColor = selectedTextColor;
    }

    void setThemeColor(int selectedThemeColor, int schemeColor) {
        this.mSelectedThemeColor = selectedThemeColor;
        this.mSchemeThemeColor = schemeColor;
    }

    int getWeekStart() {
        return mWeekStart;
    }

    void setWeekStart(int mWeekStart) {
        this.mWeekStart = mWeekStart;
    }

    int getDefaultCalendarSelectMode() {
        return mDefaultCalendarSelectMode;
    }

    void setDefaultCalendarSelectMode(int defaultCalendarSelectMode) {
        this.mDefaultCalendarSelectMode = defaultCalendarSelectMode;
    }

    int getMonthViewShowMode() {
        return mMonthViewShowMode;
    }

    void setMonthViewShowMode(int monthViewShowMode) {
        this.mMonthViewShowMode = monthViewShowMode;
    }

    int getCurrentYear() {
        return mCurrentYear;
    }

    int getCurrentMonth() {
        return mCurrentMonth;
    }

    Calendar getCurrentDay() {
        return mCurrentDate;
    }

    /**
     * 获取自定义的当前时间
     *
     * @return 自定义的当前时间对象
     */
    CustomTime getCustomTime() {
        return mCustomTime;
    }

    /**
     * 设置当前时间
     *
     * @param time 时间戳
     */
    void setCurrentTime(long time, TimeZone timeZone) {
        mCustomTime.setCurrentTime(time, timeZone);
    }

    /**
     * 更新当前时间
     */
    void updateCurrentDay() {
        java.util.Calendar calendar = mCustomTime.getCalendar();
        int day = calendar.get(java.util.Calendar.DAY_OF_MONTH);
        if (mCurrentDate != null && mCurrentDate.getDay() == day) {
            return;
        }
        Calendar currentCal = new Calendar();
        currentCal.setYear(calendar.get(java.util.Calendar.YEAR));
        currentCal.setMonth(calendar.get(java.util.Calendar.MONTH) + 1);
        currentCal.setDay(day);
        currentCal.setCurrentDay(true);
        currentCal.setCurrentWeek(true);
        CalendarUtil.setupCalendarData(currentCal);
        mCurrentDate = currentCal;
        calendar.set(java.util.Calendar.HOUR_OF_DAY, 0);
        calendar.set(java.util.Calendar.MINUTE, 0);
        calendar.set(java.util.Calendar.SECOND, 0);
        calendar.set(java.util.Calendar.MILLISECOND, 0);
        calendar.add(java.util.Calendar.DAY_OF_MONTH, CalendarUtil.getWeekFirstDayDiff(mCurrentDate, mWeekStart) * -1);
        weekStartTime = calendar.getTimeInMillis();
        calendar.add(java.util.Calendar.DAY_OF_MONTH, 6);
        calendar.set(java.util.Calendar.HOUR_OF_DAY, 23);
        calendar.set(java.util.Calendar.MINUTE, 59);
        calendar.set(java.util.Calendar.SECOND, 59);
        calendar.set(java.util.Calendar.MILLISECOND, 999);
        weekEndTime = calendar.getTimeInMillis();
    }

    void setCalendarItemHeight(int height) {
        mCalendarItemHeight = height;
    }

    int getCalendarItemHeight() {
        return mCalendarItemHeight;
    }

    int getCalendarPaddingLeft() {
        return mCalendarPaddingLeft;
    }

    void setCalendarPadding(int calendarPaddingLeft, int calendarPaddingRight) {
        this.mCalendarPaddingLeft = calendarPaddingLeft;
        this.mCalendarPaddingRight = calendarPaddingRight;
    }

    void setCalendarPaddingLeft(int mCalendarPaddingLeft) {
        this.mCalendarPaddingLeft = mCalendarPaddingLeft;
    }

    int getCalendarPaddingRight() {
        return mCalendarPaddingRight;
    }

    void setCalendarPaddingRight(int mCalendarPaddingRight) {
        this.mCalendarPaddingRight = mCalendarPaddingRight;
    }

    int getDayTextSize() {
        return mDayTextSize;
    }

    int getCurDayTextColor() {
        return mCurDayTextColor;
    }

    int getWeekTextColor() {
        return mWeekTextColor;
    }

    int getSchemeTextColor() {
        return mSchemeTextColor;
    }

    int getOtherMonthTextColor() {
        return mOtherMonthTextColor;
    }

    int getCurrentMonthTextColor() {
        return mCurrentMonthTextColor;
    }

    int getSelectedTextColor() {
        return mSelectedTextColor;
    }

    int getSchemeThemeColor() {
        return mSchemeThemeColor;
    }

    int getSelectedThemeColor() {
        return mSelectedThemeColor;
    }

    int getWeekBackground() {
        return mWeekBackground;
    }

    int getWeekTextSize() {
        return mWeekTextSize;
    }

    int getWeekBarHeight() {
        return mWeekBarHeight;
    }

    Class<?> getMonthViewClass() {
        return mMonthViewClass;
    }

    Class<?> getWeekBarClass() {
        return mWeekBarClass;
    }

    int getMinYear() {
        return mMinYear;
    }

    int getMaxYear() {
        return mMaxYear;
    }

    int getMinYearMonth() {
        return mMinYearMonth;
    }

    int getMaxYearMonth() {
        return mMaxYearMonth;
    }

    boolean isWeekSelector() {
        return mIsWeekSelector;
    }

    /**
     * 设置月起始日
     *
     * @param monthStartDay 月起始日
     * @return 月起始日是否改变，true 改变，否则false
     */
    boolean setMonthStartDay(int monthStartDay) {
        boolean isChange = mMonthStartDay != monthStartDay;
        this.mMonthStartDay = monthStartDay;
        if (isChange) {
            init();
        }
        return isChange;
    }

    int getMonthStartDay() {
        return mMonthStartDay;
    }

    Calendar getSelectedCalendar() {
        return mSelectedCalendar;
    }

    void setSelectedCalendar(Calendar selectedCalendar) {
        this.mSelectedCalendar = selectedCalendar;
    }

    /**
     * 是否是当前周
     *
     * @param calendar 日期
     * @return true 是当前周，否则false
     */
    boolean isCurrentWeek(java.util.Calendar calendar) {
        long time = calendar.getTimeInMillis();
        return time >= weekStartTime && time <= weekEndTime;
    }

    void clearSelectedScheme() {
        mSelectedCalendar.clearScheme();
    }

    /**
     * 添加数据
     *
     * @param schemeDates schemeDates
     */
    void addSchemes(Map<String, Calendar> schemeDates) {
        if (schemeDates == null || schemeDates.isEmpty()) {
            return;
        }
        if (this.mSchemeDatesMap == null) {
            this.mSchemeDatesMap = new HashMap<>();
        }
        for (String key : schemeDates.keySet()) {
            this.mSchemeDatesMap.remove(key);
            Calendar calendar = schemeDates.get(key);
            if (calendar == null) {
                continue;
            }
            this.mSchemeDatesMap.put(key, calendar);
        }
    }
}
